/***************************************************************************
 *   Copyright (C) 2007 by Autostop team                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

// Qt
#include <QFile>

// local
#include "gencryptor.h"
#include "gexception.h"

// ============================= GEncryptor ==========================
GEncryptor::GEncryptor()
{
	// nope
}

// ============================= ~GEncryptor ==========================
GEncryptor::~GEncryptor()
{
	// nope
}

// ============================= setKey ==========================
/// Sets the encription key
void GEncryptor::setKey( const QByteArray& key )
{
	blowfishCreateContext( key );
}

// ============================= setKeyFromFile ==========================
/// Loads encruption key from file. Entire file content is used as a key.
/// If file can not be open, GSysError is thrown.
void GEncryptor::setKeyFromFile( const QString& path )
{
	QFile keyFile( path );
	if ( ! keyFile.open( QIODevice::ReadOnly ) )
	{
		throw GSysError("Can not read key file " + path + ":" + keyFile.errorString() );
	}
	
	blowfishCreateContext( keyFile.readAll() );
}

// ============================= encrypt ==========================
/// Encrypts data block with previously set key. Throws GConfigurationError if 
/// key is not set or invalid.
/// Output data block will be 4 to 11 bytes longer than input
QByteArray GEncryptor::encrypt( const QByteArray& data )
{
	// TODO check if key initialized
	
	// make sure encrypted buffer is 8-byte aligned
	QByteArray block( data );
	while ( block.size() & 7 )
	{
		// add one byte, initialize with zero
		block.resize( block.size() + 1 );
		block[ block.size() - 1 ] = 0;
	}
	
	qDebug("encoded block size changed from %d to %d", data.size(), block.size() ); // TODO remove
	
	blowfish_encrypt_cbc( (uint8_t*)block.data(), block.size(), &_blowfishContext );
	
	// append original size to block
	qint32 originalSize = data.size();
	block.append( QByteArray( (const char*)&originalSize, 4 ) );
	return block;
}

// ============================= decrypt ==========================
/// Decrypts data block with previously set key. Throws GConfigurationError if 
/// key is not set or invalid
QByteArray GEncryptor::decrypt( const QByteArray& encrypted )
{
	// TODO check if key initialized
	
	// restore oryginal size
	qint32 originalSize = 0;
	originalSize = * ( (qint32*)( encrypted.data() + encrypted.size() - 4 ) );
	qDebug("original size restored: %d", originalSize );
	
	QByteArray block( encrypted );
	
	blowfish_decrypt_cbc( (uint8_t*)block.data(),
				 block.size() - 4, &_blowfishContext );
	
	// truencate to original; size
	block.resize( originalSize );
	return block;
}

// ============================= blowfishCreateContext ==========================
/// Creates Blowfish context from supplied key
void GEncryptor::blowfishCreateContext( const QByteArray& key )
{
	// max and min key lengths
	const int BLOWFISH_KEY_MIN	=	8;
	const int BLOWFISH_KEY_MAX	=	56;
	
	// check key
	if ( key.isNull() || key.length() < BLOWFISH_KEY_MIN )
	{
		throw GConfigurationError("Key invalid or too short for Blowfish encryption" );
	}
	
	// init context
	int keyLength = qMin( key.length(), BLOWFISH_KEY_MAX );
	// make sure key is /8
	keyLength -= ( keyLength % 8 );

	qDebug( "GEncryptor::blowfishCreateContext: Key length: %d", keyLength );
	blowfish_setkey( &_blowfishContext, (const uint8_t*) key.data(), keyLength  );
	
	// set some iv
	blowfish_setiv( &_blowfishContext, (const uint8_t*)"abcd1234" ); // some 8-byte string
	
}

// eof

